package p.ithorns.tools.translator.sdk.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import p.ithorns.tools.translator.annotation.Translatable;
import p.ithorns.tools.translator.annotation.Translator;
import p.ithorns.tools.translator.sdk.exception.TranslateException;
import p.ithorns.tools.translator.sdk.utils.LambdaUtil;
import p.ithorns.tools.translator.sdk.utils.ReflectUtil;
import p.ithorns.tools.translator.sdk.utils.SpringUtil;
import p.ithorns.tools.translator.sdk.utils.UnSafeUtil;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * 转换工具类
 * 带缓存增强的版本
 *
 * @author ithorns
 * @date 2023-04-17 16:25
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class TranslateCore {

    private final static Logger log = LoggerFactory.getLogger(TranslateCore.class);


    private static final Map<Class<?>, Translator> TRANSLATORS = new HashMap<>(8);

    /**
     * [对象字段-字典]映射
     */
    final static ThreadLocal<Map<Field, Map<String, Serializable>>> DICT_CODE_MAP = new ThreadLocal<>();

    /**
     * [对象字段-字典值]映射
     */
    private final static ThreadLocal<Map<Field, Map<Object, Object>>> DICT_VALUE_MAP = new ThreadLocal<>();


    static {
        ApplicationContext context = SpringUtil.getContext();
        Map<String, Translator> beansOfType = context.getBeansOfType(Translator.class);
        Set<Map.Entry<String, Translator>> entries = beansOfType.entrySet();
        for (Map.Entry<String, Translator> entry : entries) {
            Translator translator = entry.getValue();
            Class<?> annotationType = getAnnotationTypeOfTranslator(translator);
            if (isTypeOfTranslatable(annotationType)) {
                TRANSLATORS.put(translator.getClass(), translator);
            } else {
                TRANSLATORS.put(annotationType, translator);
            }
        }

        UnSafeUtil.replaceModule();
    }

    public static void translate(Object object, Translatable meta) {
        DICT_CODE_MAP.set(new ConcurrentHashMap<>(64));
        DICT_VALUE_MAP.set(new ConcurrentHashMap<>(64));
        try {
            doTranslate(object, meta);
        } finally {
            DICT_VALUE_MAP.remove();
            DICT_CODE_MAP.remove();
        }
    }

    /**
     * 转换对象，支持集合(Collection)或者单个bean
     * 并直接改变对象内部属性
     *
     * @param object 集合或单个bean
     */
    public static void doTranslate(Object object, Translatable meta) {
        if (object == null) {
            return;
        }
        if (log.isDebugEnabled()) {
            log.debug("[Translator]-> 待转换对象类型{}", object.getClass().getName());
        }
        if (object instanceof Collection collection) {
            // 只有集合需要批量转换
            readDictCodes(collection);
            writeDictValues(collection);
        } else {
            readAndWriteDictOne(object);
        }
    }

    /**
     * 批量读取字典
     */
    private static void readDictCodes(Collection collection) {
        for (Object bean : collection) {
            FieldInfo[] fieldInfos = ReflectUtil.getFieldInfos(bean);

            // 批量读取字典值
            for (FieldInfo fi : fieldInfos) {
                if (fi.isDictMetaField()) {
                    Field field = fi.getField();
                    String fieldName = field.getName();
                    // 转换嵌套字段，字段上需要标注@Translatable且字段类型不为String
                    if (isAnnotatedByTranslatable(field)) {
                        // 递归转换
                        Object fieldValue = LambdaUtil.sure(() -> ReflectUtil.invokeReadMethod(bean, fieldName));
                        if (log.isDebugEnabled()) {
                            log.debug("[Translator]-> [{}]是Translatable字段，内部递归", ReflectUtil.makeClassFieldName(field));
                        }

                        if (null != fieldValue) {
                            readDictCodes((fieldValue instanceof Collection)
                                    ? (Collection<?>) fieldValue : Collections.singleton(fieldValue));
                        }
                        continue;
                    }

                    // 字典code值
                    Serializable dict = getFieldDict(fi, bean);
                    if (null != dict) {
                        cacheDictCodes(bean, field, dict);
                    }
                }
            }
        }
    }

    /**
     * 批量写入字典值
     */
    private static void writeDictValues(Collection<?> collection) {
        // 批量写入字段值
        final BiFunction<FieldInfo, Object, Object> getFieldValue = getFieldValueFunc();
        for (Object bean : collection) {
            FieldInfo[] fieldInfos = ReflectUtil.getFieldInfos(bean);
            for (FieldInfo fi : fieldInfos) {
                Field field = fi.getField();
                String fieldName = field.getName();

                if (isAnnotatedByTranslatable(field)) {
                    // 递归写入
                    Object transField = ReflectUtil.invokeReadMethodSafe(bean, fieldName);
                    if (null != transField) {
                        writeDictValues((transField instanceof Collection)
                                ? (Collection<?>) transField : Collections.singleton(transField));
                    }
                    continue;
                }

                Object val = getFieldValue.apply(fi, bean);

                if (null == val) {
                    // 设置默认值
                    Translatable translatable = fi.getTranslatable();
                    if (null != translatable) {
                        val = translatable.defVal();
                    }
                }

                ReflectUtil.invokeWriteMethodSafe(bean, field, val);
            }
        }
    }

    private static void readAndWriteDictOne(Object bean) {
        FieldInfo[] fieldTransInfos = ReflectUtil.getFieldInfos(bean);
        // 循环处理需要转换的字段，字段上的注解链上需要有@Translatable，且字段类型必须为String
        for (FieldInfo fi : fieldTransInfos) {
            if (fi.isDictMetaField()) {// 只转换String类型的属性，其他类型的属性代表是嵌套情况需要过滤掉，后面处理
                final Field field = fi.getField();
                final Translatable translatable = fi.getTranslatable();

                // 转换嵌套字段，字段上需要标注@Translatable且字段类型不为String
                if (isAnnotatedByTranslatable(field)) {
                    // 递归转换
                    Object transField = ReflectUtil.invokeReadMethodSafe(bean, field.getName());
                    if (log.isDebugEnabled()) {
                        log.info("[Translator]-> [{}]是Translatable字段, 内部递归", ReflectUtil.makeClassFieldName(field));
                    }
                    doTranslate(transField, translatable);
                    continue;
                }

                String dictFieldName = getSourceFieldName(bean, field, translatable);
                Serializable dictCode = ReflectUtil.invokeReadMethodSafe(bean, dictFieldName);
                if (dictCode == null) {
                    return;
                }

                Annotation dictMeta = fi.getMeta();
                Translator translator = getTranslator(dictMeta);
                // 获取转换器及转换器绑定的注解
                if (null == translator) {
                    return;
                }

                String result = translator.translate(dictMeta, dictCode);
                if (StringUtils.hasText(result)) {
                    // 转换结果写入当前字段
                    ReflectUtil.invokeWriteMethodSafe(bean, field, result);
                }
                if (log.isDebugEnabled()) {
                    log.info("[Translator]-> 直接读取并转换字段[{}]字典: {}-{}", ReflectUtil.makeClassFieldName(field), dictCode, result);
                }
            }
        }
    }

    /**
     * 通过注解类型获取转换器
     */
    private static Translator<?, Annotation> getTranslator(Annotation dictMeta) {
        Class<? extends Annotation> type = dictMeta.annotationType();
        if (isTypeOfTranslatable(type)) {
            Class<?> annotatedTranslator = (Class<?>) getAnnotatedTranslator(dictMeta);
            return TRANSLATORS.get(annotatedTranslator);
        } else {
            return TRANSLATORS.get(type);
        }
    }

    /**
     * 对象字典字段加入缓存
     */
    private static void cacheDictCodes(Object bean, Field field, Serializable dict) {
        Map<Field, Map<String, Serializable>> fieldSetMap = DICT_CODE_MAP.get();
        Map<String, Serializable> keys = fieldSetMap.computeIfAbsent(field, f -> new HashMap<>());
        String ref = ReflectUtil.makeBeanFieldRef(field, bean);
        keys.put(ref, dict);
    }

    /**
     * 从字段缓存获取字典值
     *
     * @return BiFunction<FieldInfo, bean, dictValue>
     */
    private static BiFunction<FieldInfo, Object, Object> getFieldValueFunc() {
        return (fi, bean) -> {
            Field field = fi.getField();

            // 获取字典引用
            Map<Field, Map<String, Serializable>> dictCodeMap = DICT_CODE_MAP.get();
            Map<String, ? extends Serializable> codeMap = dictCodeMap.get(field);
            if (null == codeMap) {
                return null;
            }
            String ref = ReflectUtil.makeBeanFieldRef(field, bean);
            Serializable dictCode = codeMap.get(ref);

            // 获取字典缓存值
            Map<Field, Map<Object, Object>> tempMap = DICT_VALUE_MAP.get();
            Map<Object, Object> dictValueMap = tempMap.computeIfAbsent(field, k -> {
                final Function<FieldInfo, Map<Object, Object>> dictValueFunc = getDictValueFunc();
                return dictValueFunc.apply(fi);
            });
            return dictValueMap.get(dictCode);
//            if (log.isDebugEnabled()) {
//                log.debug("[Translator]-> 转换字段值[{}]: {}-{}", makeClassFieldName(field), dictCode, val);
//            }
        };
    }


    /**
     * 字典转换器批量转换
     *
     * @return Function<FieldInfo, Map < dictCode, dictValue>>
     */
    private static Function<FieldInfo, Map<Object, Object>> getDictValueFunc() {
        return fi -> {
            Annotation dictMeta = fi.getMeta();
            Translator<?, Annotation> translator = getTranslator(dictMeta);
            if (null == translator) {
                return Collections.emptyMap();
            }

            Field field = fi.getField();
            Map<Field, Map<String, Serializable>> dictKeysMap = DICT_CODE_MAP.get();
            Map dictCodeMap = dictKeysMap.get(field);
            Collection dictCodes = dictCodeMap.values();

            if (log.isDebugEnabled()) {
                log.info("[Translator]-> 批量读取字段[{}]字典：{}", ReflectUtil.makeClassFieldName(field), dictCodes);
            }
            try {
                return translator.translate(dictMeta, new HashSet<>(dictCodes));
            } catch (Exception e) {
                throw new TranslateException(fi.getFqn() + e.getMessage(), e);
            }
        };
    }


    private static <T extends Serializable> T getFieldDict(FieldInfo fi, Object bean) {
        final Translatable translatable = fi.getTranslatable();
        Field field = fi.getField();
        String sourceField = getSourceFieldName(bean, field, translatable);
        return LambdaUtil.sure(() -> ReflectUtil.invokeReadMethod(bean, sourceField));
    }


    /**
     * 字段是否被@Translatable元注解
     *
     * @param field -字段
     */
    private static boolean isAnnotatedByTranslatable(Field field) {
        return field.getType() != String.class && field.isAnnotationPresent(Translatable.class);
    }

    /**
     * 是否直接是元注解Translatable
     */
    private static boolean isTypeOfTranslatable(Class<?> type) {
        return Translatable.class.equals(type);
    }

    private static Class<?> getAnnotationTypeOfTranslator(Translator<?, Annotation> translator) {
        // Translatable上有两个泛型，第一个是转换前的值类型，第二个是是自定义注解类型
        Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(translator.getClass(), Translator.class);
        Assert.isTrue(genericTypes != null, "转换错误！实现Translator接口必须指定泛型："
                + translator.getClass().getSimpleName());
        return genericTypes[1];
    }

    /**
     * 获取字典字段名称
     * 如果没有显示指定将自动推断
     */
    private static String getSourceFieldName(Object bean, Field field, Translatable translatable) {
        String from = translatable.from();
        if (StringUtils.hasText(from)) {
            return from;
        }

        // 从代理获取
        from = (String) getAnnotatedFrom(translatable);
        if (StringUtils.hasText(from)) {
            return from;
        }

        // 没设置from值，智能推断关联的属性名，可能为xx、xxId、xxCode
        final String transFieldName = field.getName();
        Set<String> possibleNames = new HashSet<>(8);
        possibleNames.add(StringUtils.replace(transFieldName, "Name", ""));
        possibleNames.add(StringUtils.replace(transFieldName, "Name", "Id"));
        possibleNames.add(StringUtils.replace(transFieldName, "Name", "Code"));

        // 匹配bean属性列表
        FieldInfo[] fieldInfos = ReflectUtil.getFieldInfos(bean);
        String fieldName = Arrays.stream(fieldInfos)
                .map(fi -> fi.getField().getName())
                .filter(possibleNames::contains)
                .findFirst()
                .orElse(transFieldName);

        // 更新注解属性值，相当于做了个缓存
        UnSafeUtil.updateAnnotationProxy(translatable, "from", fieldName);
        return fieldName;
    }

    private static Object getAnnotatedFrom(Annotation translatable) {
        Map<String, Object> map = UnSafeUtil.getProxyFields(translatable);
        return map.get("from");
    }

    private static Object getAnnotatedTranslator(Annotation annotation) {
        Map<String, Object> map = UnSafeUtil.getProxyFields(annotation);
        return map.get("translator");
    }


}